package com.k8.common.aop;

import com.k8.common.util.AopUtil;
import com.k8.rpc.url.URL;
import com.k8.common.aop.internal.advisor.Advisor;
import net.sf.cglib.proxy.*;

import java.lang.reflect.Method;
import java.util.List;

//name:cglib
public class CglibProxyFactory implements ProxyFactory {
    @Override
    public Object build(Object target, List<Advisor> cacheAdvisors) {
        boolean proxyNecessary = AdvisedSupport.isProxyNecessary(target, cacheAdvisors);
        if (!proxyNecessary) {
            AdvisedSupport.cacheCandidates.remove();
            return target;
        }
        AdvisedSupport advisedSupport = null;
        advisedSupport = new CglibAdvisedSupport(target);
        CglibAopProxy cglibAopProxy = new CglibAopProxy(advisedSupport);
        return cglibAopProxy.getProxy();
    }

    @Override
    public Object build(URL url, Object target, List<Advisor> cacheAdvisors) {
        return build(target, cacheAdvisors);
    }

    @Override
    public <T> T createInterfaceProxy(Class<T> clazz, InterfaceProxyMethodInterceptor interceptor) {
        return createInterfaceProxy(clazz, determineClassLoader(clazz.getClassLoader()), interceptor);
    }

    @Override
    public <T> T createInterfaceProxy(Class<T> clazz, ClassLoader classLoader, InterfaceProxyMethodInterceptor interceptor) {
        if (!clazz.isInterface()) throw new RuntimeException("Class is not a interface , class: " + clazz.getName());
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(classLoader);
        enhancer.setInterfaces(new Class[]{clazz});
        enhancer.setAttemptLoad(true);
        enhancer.setClassLoader(classLoader);
        CallbackFilter callbackFilter = new CallbackFilter() {
            public int accept(Method method) {
                if (AopUtil.isNoOpMethod(method)) {
                    return 1;
                }
                return 0;
            }
        };
        enhancer.setCallbackFilter(callbackFilter);
        Callback[] callbacks = this.getCallbacks(interceptor);
        enhancer.setCallbacks(callbacks);
        return (T) enhancer.create();
    }

    private Callback[] getCallbacks(InterfaceProxyMethodInterceptor interceptor) {
        Callback[] callbacks = new Callback[2];
        callbacks[0] = new DefaultMethodInterceptor(interceptor);
        callbacks[1] = new CglibAopProxy.SerializableNoOp();
        return callbacks;
    }

    private ClassLoader determineClassLoader(ClassLoader classLoader) {
        if (classLoader == null) {
            return this.getClass().getClassLoader();
        } else {
            if (classLoader.getParent() == null) {
                ClassLoader aopClassLoader = this.getClass().getClassLoader();

                for (ClassLoader aopParent = aopClassLoader.getParent(); aopParent != null; aopParent = aopParent.getParent()) {
                    if (classLoader == aopParent) {
                        return aopClassLoader;
                    }
                }
            }
            return classLoader;
        }
    }

    //桥接器，将自定以拦截器桥接到Cglib的拦截器下
    public static class DefaultMethodInterceptor implements MethodInterceptor {
        InterfaceProxyMethodInterceptor interceptor;

        public DefaultMethodInterceptor(InterfaceProxyMethodInterceptor interceptor) {
            this.interceptor = interceptor;
        }

        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            return interceptor.process(proxy, method, args);
        }
    }
}
